#ifndef _FUNCTOR_H_
#define _FUNCTOR_H_

// Inspired by "Modern C++ Design"
// almost kill me at time when compiled

#include <memory>
#include "typelist.hpp"
#include "util.hpp"

namespace Medved
{

template <class T>
struct ParamType
{
	typedef const T& Result;
};

template <class T>
struct ParamType<T &>
{
	typedef T& Result;
};

template <class U>
U *makeClone(U *p)
{
	if (!p)
		return 0;
	return static_cast<U *>(p->clone());
}

template <class Ret>
struct FunctorImplBase
{
	typedef Ret ResultType;
	typedef NullType Param1;
	typedef NullType Param2;
	typedef NullType Param3;
	typedef NullType Param4;

	virtual FunctorImplBase *clone() const = 0;
	virtual ~FunctorImplBase() {};

};

template <class Ret, class Args>
struct FunctorImpl;

template <class Ret>
class FunctorImpl<Ret, NullType> : public FunctorImplBase<Ret>
{
public:
	typedef Ret ResultType;

	virtual Ret operator()() = 0;
};

template <class Ret, class T1>
class FunctorImpl<Ret, TYPELIST_1(T1) > : public FunctorImplBase<Ret>
{
public:
	typedef Ret ResultType;
	typedef typename ParamType<T1>::Result Param1;

	virtual Ret operator()(Param1) = 0;
};

template <class Ret, class T1, class T2>
class FunctorImpl<Ret, TYPELIST_2(T1, T2) > : public FunctorImplBase<Ret>
{
public:
	typedef Ret ResultType;
	typedef typename ParamType<T1>::Result Param1;
	typedef typename ParamType<T2>::Result Param2;

	virtual Ret operator()(Param1, Param2) = 0;
};


template <class Ret, class T1, class T2, class T3>
class FunctorImpl<Ret, TYPELIST_3(T1, T2, T3) > : public FunctorImplBase<Ret>
{
public:
	typedef Ret ResultType;
	typedef typename ParamType<T1>::Result Param1;
	typedef typename ParamType<T2>::Result Param2;
	typedef typename ParamType<T3>::Result Param3;

	virtual Ret operator()(Param1, Param2, Param3) = 0;
};

template <class Ret, class T1, class T2, class T3, class T4>
class FunctorImpl<Ret, TYPELIST_4(T1, T2, T3, T4) > : public FunctorImplBase<Ret>
{
public:
	typedef Ret ResultType;
	typedef typename ParamType<T1>::Result Param1;
	typedef typename ParamType<T2>::Result Param2;
	typedef typename ParamType<T3>::Result Param3;
	typedef typename ParamType<T3>::Result Param4;

	virtual Ret operator()(Param1, Param2, Param3, Param4) = 0;
};

template <class ParentFunctor, class Fun>
class FunctorHolder : public ParentFunctor::Impl
{
	typedef typename ParentFunctor::Impl Base;

public:
	typedef typename Base::ResultType ResultType;
	typedef typename Base::Param1 Param1;
	typedef typename Base::Param2 Param2;
	typedef typename Base::Param3 Param3;
	typedef typename Base::Param4 Param4;

	FunctorHolder(Fun fun) :
		fun_(fun)
	{}

	FunctorHolder *clone() const { return new FunctorHolder(*this); }

	ResultType operator()() { return fun_(); }
	ResultType operator()(Param1 p1) { return fun_(p1); }
	ResultType operator()(Param1 p1, Param2 p2) { return fun_(p1, p2); }
	ResultType operator()(Param1 p1, Param2 p2, Param3 p3) { return fun_(p1, p2, p3); }
	ResultType operator()(Param1 p1, Param2 p2, Param3 p3, Param4 p4) { return fun_(p1, p2, p3, p4); }

private:
	Fun fun_;
};

template <class ParentFunctor, class Holder, class Method>
class MemFunHolder : public ParentFunctor::Impl
{
	typedef typename ParentFunctor::Impl Base;

public:
	typedef typename Base::ResultType ResultType;
	typedef typename Base::Param1 Param1;
	typedef typename Base::Param2 Param2;
	typedef typename Base::Param3 Param3;
	typedef typename Base::Param4 Param4;


	MemFunHolder(Holder *h, const Method m) :
		h_(h), m_(m)
	{}

	MemFunHolder *clone() const { return new MemFunHolder(*this); }

	ResultType operator()() { return (h_->*m_)(); }
	ResultType operator()(Param1 p1) { return (h_->*m_)(p1); }
	ResultType operator()(Param1 p1, Param2 p2) { return (h_->*m_)(p1, p2); }
	ResultType operator()(Param1 p1, Param2 p2, Param3 p3) { return (h_->*m_)(p1, p2, p3); }
	ResultType operator()(Param1 p1, Param2 p2, Param3 p3, Param4 p4) { return (h_->*m_)(p1, p2, p3, p4); }

private:
	Holder *h_;
	Method m_;
};

template <class ParentFunctor>
class BinderHolder : public FunctorImpl<typename ParentFunctor::ResultType, NullType>
{
	typedef typename ParentFunctor::Impl Base;

public:
	typedef typename Base::ResultType ResultType;
	typedef typename Base::Param1 Param1;
	typedef typename Base::Param2 Param2;
	typedef typename Base::Param3 Param3;
	typedef typename Base::Param4 Param4;
	// define real parameters types
	// unless it will be references
	typedef typename ParentFunctor::Param1 Param1Type;
	typedef typename ParentFunctor::Param2 Param2Type;
	typedef typename ParentFunctor::Param3 Param3Type;
	typedef typename ParentFunctor::Param4 Param4Type;

	BinderHolder(const ParentFunctor& h, Param1 p1) :
		h_(h), p1_(p1)
	{}

	BinderHolder(const ParentFunctor& h, Param1 p1, Param2 p2) :
		h_(h), p1_(p1), p2_(p2)
	{}

	BinderHolder(const ParentFunctor& h, Param1 p1, Param2 p2, Param3 p3) :
		h_(h), p1_(p1), p2_(p2), p3_(p3)
	{}

	BinderHolder(const ParentFunctor& h, Param1 p1, Param2 p2, Param3 p3, Param4 p4) :
		h_(h), p1_(p1), p2_(p2), p3_(p3), p4_(p4)
	{}

	BinderHolder *clone() const { return new BinderHolder(*this); }

	ResultType call(Int2Type<1>) {return h_(p1_);}
	ResultType call(Int2Type<2>) {return h_(p1_, p2_);}
	ResultType call(Int2Type<3>) {return h_(p1_, p2_, p3_);}
	ResultType call(Int2Type<4>) {return h_(p1_, p2_, p3_, p4_);}

	// redirects to proper call
	ResultType operator()()
	{
		return call(Int2Type<Length<typename ParentFunctor::Args>::Result>());
	}

private:
	ParentFunctor h_;
	Param1Type p1_;
	Param2Type p2_;
	Param3Type p3_;
	Param4Type p4_;
};

template <class Ret = void, class P1 = NullType, class P2 = NullType,
			class P3 = NullType, class P4 = NullType>
class Functor
{
public:
	typedef typename Seq2TypeList<Seq<P1, P2, P3, P4> >::Result Args;
	typedef FunctorImpl<Ret, Args> Impl;
	typedef Ret ResultType;
	typedef typename TypeAt<Args, 0>::Result Param1;
	typedef typename TypeAt<Args, 1>::Result Param2;
	typedef typename TypeAt<Args, 2>::Result Param3;
	typedef typename TypeAt<Args, 3>::Result Param4;

	Functor() : 
		impl_(0)
	{}

	Functor(const Functor& rhs) :
		impl_(makeClone(rhs.impl_.get()))
	{}

	Functor(Impl *impl) :
		impl_(impl)
	{}

	template <class Holder, class Method>
	Functor(Holder &h, Method m) :
		impl_(new MemFunHolder<Functor, Holder, Method>(&h, m))
	{}

	template <class Fun>
	Functor(const Fun fun) :
		impl_(new FunctorHolder<Functor, Fun>(fun))
	{}

	// yes, by value
	Functor& operator =(Functor rhs)
	{
		Impl *tmp;

		tmp = rhs.impl_.release();
		rhs.impl_.reset(impl_.release());
		impl_.reset(tmp);
		return *this;
	}

	Impl *getImpl() const { return impl_.get(); }
	ResultType operator()() const { return (*impl_)(); }
	ResultType operator()(Param1 p1) const { return (*impl_)(p1); }
	ResultType operator()(Param1 p1, Param2 p2) const { return (*impl_)(p1, p2); }
	ResultType operator()(Param1 p1, Param2 p2, Param3 p3) const { return (*impl_)(p1, p2, p3); }
	ResultType operator()(Param1 p1, Param2 p2, Param3 p3, Param4 p4) const { return (*impl_)(p1, p2, p3, p4); }

private:
	std::auto_ptr<Impl> impl_;
};

template <class Fun>
Functor<typename Fun::ResultType> bind(const Fun &fun, typename Fun::Param1 p1)
{
	// we need explicit cast here to make proper constructor called, not templated
	// proper constructor is Functor(Impl *impl)
	return Functor<typename Fun::ResultType>(static_cast<typename Functor<typename Fun::ResultType>::Impl *>(
			new BinderHolder<Fun>(fun, p1)));
}

template <class Fun>
Functor<typename Fun::ResultType> bind(const Fun &fun, typename Fun::Param1 p1, typename Fun::Param2 p2)
{
	// we need explicit cast here to make proper constructor called, not templated
	// proper constructor is Functor(Impl *impl)
	return Functor<typename Fun::ResultType>(static_cast<typename Functor<typename Fun::ResultType>::Impl *>(
			new BinderHolder<Fun>(fun, p1, p2)));
}

template <class Fun>
Functor<typename Fun::ResultType> bind(const Fun &fun, typename Fun::Param1 p1, typename Fun::Param2 p2,
		typename Fun::Param3 p3)
{
	// we need explicit cast here to make proper constructor called, not templated
	// proper constructor is Functor(Impl *impl)
	return Functor<typename Fun::ResultType>(static_cast<typename Functor<typename Fun::ResultType>::Impl *>(
			new BinderHolder<Fun>(fun, p1, p2, p3)));
}

template <class Fun>
Functor<typename Fun::ResultType> bind(const Fun &fun, typename Fun::Param1 p1, typename Fun::Param2 p2,
		typename Fun::Param3 p3, typename Fun::Param4 p4)
{
	// we need explicit cast here to make proper constructor called, not templated
	// proper constructor is Functor(Impl *impl)
	return Functor<typename Fun::ResultType>(static_cast<typename Functor<typename Fun::ResultType>::Impl *>(
			new BinderHolder<Fun>(fun, p1, p2, p3, p4)));
}

template <class R, class P1>
Functor<R> bind(R (&fun)(P1), typename ParamType<P1>::Result p1)
{
	return bind(Functor<R, P1>(fun), p1);
}

template <class R, class P1, class P2>
Functor<R> bind(R (&fun)(P1, P2), typename ParamType<P1>::Result p1, typename ParamType<P2>::Result p2)
{
	return bind(Functor<R, P1, P2>(fun), p1, p2);
}

template <class R, class P1, class P2, class P3>
Functor<R> bind(R (&fun)(P1, P2, P3), typename ParamType<P1>::Result p1, typename ParamType<P2>::Result p2,
		typename ParamType<P3>::Result p3)
{
	return bind(Functor<R, P1, P2, P3>(fun), p1, p2, p3);
}

template <class R, class P1, class P2, class P3, class P4>
Functor<R> bind(R (&fun)(P1, P2, P3, P4), typename ParamType<P1>::Result p1, typename ParamType<P2>::Result p2,
		typename ParamType<P3>::Result p3, typename ParamType<P4>::Result p4)
{
	return bind(Functor<R, P1, P2, P3, P4>(fun), p1, p2, p3, p4);
}

template <class R>
Functor<R> makeFunctor(R (&fun)())
{
	return Functor<R>(fun);
}

template <class R, class P1>
Functor<R, P1> makeFunctor(R (&fun)(P1))
{
	return Functor<R, P1>(fun);
}

template <class R, class P1, class P2>
Functor<R, P1, P2> makeFunctor(R (&fun)(P1, P2))
{
	return Functor<R, P1, P2>(fun);
}

template <class R, class P1, class P2, class P3>
Functor<R, P1, P2, P3> makeFunctor(R (&fun)(P1, P2, P3))
{
	return Functor<R, P1, P2, P3>(fun);
}

template <class R, class P1, class P2, class P3, class P4>
Functor<R, P1, P2, P3, P4> makeFunctor(R (&fun)(P1, P2, P3, P4))
{
	return Functor<R, P1, P2, P3, P4>(fun);
}

template <class H, class R>
Functor<R> makeFunctor(H &holder, R (H::*method)())
{
	return Functor<R>(holder, method);
}

template <class H, class R, class P1>
Functor<R, P1> makeFunctor(H &holder, R (H::*method)(P1))
{
	return Functor<R, P1>(holder, method);
}

template <class H, class R, class P1, class P2>
Functor<R, P1, P2> makeFunctor(H &holder, R (H::*method)(P1, P2))
{
	return Functor<R, P1, P2>(holder, method);
}

template <class H, class R, class P1, class P2,  class P3>
Functor<R, P1, P2, P3> makeFunctor(H &holder, R (H::*method)(P1, P2, P3))
{
	return Functor<R, P1, P2, P3>(holder, method);
}

template <class H, class R, class P1, class P2,  class P3, class P4>
Functor<R, P1, P2, P3, P4> makeFunctor(H &holder, R (H::*method)(P1, P2, P3, P4))
{
	return Functor<R, P1, P2, P3, P4>(holder, method);
}

}

#endif
